package com.open.rbac.config.resolver;

import java.util.Map;
import java.util.Optional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.core.MethodParameter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver;

import com.open.center.common.constant.OpenConstant;
import com.open.center.jwt.model.JwtUser;
import com.open.center.jwt.provider.JwtProvider;
import com.open.rbac.config.annotation.TokenUserId;

/**
 * 令牌解析器
 *
 * @author Riche's
 * @since 2025/6/13
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TokenResolver extends AbstractNamedValueMethodArgumentResolver {

    private final JwtProvider jwtProvider;

    @NonNull
    @Override
    protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
        TokenUserId annotation = parameter.getParameterAnnotation(TokenUserId.class);
        return annotation != null ? new TokenResolver.TokenNamedValueInfo(annotation) : new TokenResolver.TokenNamedValueInfo();
    }

    @Override
    protected Object resolveName(@NonNull String name, @NonNull MethodParameter parameter, @NonNull NativeWebRequest request) throws Exception {
        Optional<String> optional = resolveToken(request);
        if (optional.isPresent()) {
            JwtUser jwtUser = jwtProvider.parseUser(optional.get());
            log.info("成功解析Token: userId = {}, username = {}", jwtUser.getUserId(), jwtUser.getUsername());
            return jwtUser.getUserId();
        }
        return null;
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.hasParameterAnnotation(TokenUserId.class)) {
            if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
                return true;
            } else {
                TokenUserId tokenUserId = parameter.getParameterAnnotation(TokenUserId.class);
                return tokenUserId != null && StringUtils.hasText(tokenUserId.name());
            }
        }
        return true;
    }

    private Optional<String> resolveToken(NativeWebRequest request) {
        String authorizationToken = request.getHeader(OpenConstant.AUTHORIZATION);
        return OpenConstant.resolveToken(authorizationToken);
    }

    private static class TokenNamedValueInfo extends NamedValueInfo {
        public TokenNamedValueInfo() {
            super("", false, ValueConstants.DEFAULT_NONE);
        }

        private TokenNamedValueInfo(TokenUserId annotation) {
            super(annotation.name(), annotation.required(), annotation.defaultValue());
        }
    }
}
